package gdma.hqdes.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import common.util.TreeUtils;
import gdma.hqdes.bean.*;
import gdma.hqdes.dao.*;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import sindconsole.misc.service.DictService;

import javax.annotation.Nullable;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author wangyue
 * @Version 3.0
 * @Date 2024-07-13
 */

@Service
public class IndexServiceImpl implements IndexService {

    @Autowired
    HqdProjectMapper hqdProjectMapper;

    @Autowired
    HqdIndexVerMapper hqdIndexVerMapper;

    @Autowired
    HqdIndexMapper hqdIndexMapper;

    @Autowired
    HqdIndexExtMapper hqdIndexExtMapper;

    @Autowired
    HqdIndexArgMapper hqdIndexArgMapper;

    @Autowired
    UnitScoreMapper unitScoreMapper;

    @Autowired
    StatIndexMapper statIndexMapper;

    @Autowired
    StatIndexItemMapper statIndexItemMapper;

    @Autowired
    UnitService unitService;

    @Autowired
    DictService dictService;

    @Override
    public HqdIndex getIndex(long id) {
        HqdIndex hqdIndex = hqdIndexMapper.selectById(id);
        if(hqdIndex.getLevel() == 3) {
            hqdIndex.setIndexExts(hqdIndexExtMapper.selectByIndexId(id));
            hqdIndex.setIndexArgs(hqdIndexArgMapper.selectByIndexId(id));
            for(HqdIndexArg indexArg: hqdIndex.getIndexArgs()) {
                if(indexArg.getValueOptionDictId() != null && indexArg.getValueOptionDictId() > 0) {
                    indexArg.setValueOptions(listArgOptions(indexArg.getValueOptionDictId()));
                }
            }
        }
        return hqdIndex;
    }

    @Override
    public List<HqdIndex> getThinIndexes(Long... ids) {
        LambdaQueryWrapper<HqdIndex> query = new QueryWrapper<HqdIndex>().lambda();
        if(!ArrayUtils.isEmpty(ids)) query.in(HqdIndex::getId, ids);
        return hqdIndexMapper.selectList(query);
    }

    @Override
    public List<HqdIndex> listThinIndexes(@Nullable Collection<Integer> verIds, @Nullable Collection<String> codes, @Nullable String keyword) {
        LambdaQueryWrapper<HqdIndex> query = new QueryWrapper<HqdIndex>().lambda();
        if(!CollectionUtils.isEmpty(verIds)) query.in(HqdIndex::getVerId, verIds);
        if(!CollectionUtils.isEmpty(codes)) query.in(HqdIndex::getCode, codes);
        if(StringUtils.hasText(keyword))
            query.and(q -> q.eq(HqdIndex::getCode, keyword).or().like(HqdIndex::getName, keyword));
        return hqdIndexMapper.selectList(query);
    }

    private List<String[]> listArgOptions(Integer valueOptionDictId) {
        return dictService.findDictItems(valueOptionDictId).stream()
                .map(di -> new String[] { di.getLabel(), di.getValue() })
                .collect(Collectors.toList());
    }

    @Override
    public HqdIndex buildThinIndexTree(long verId) {
        List<HqdIndex> indexList = hqdIndexMapper.selectByVerId(verId);
        if(indexList.isEmpty()) return null;
        TreeUtils.buildTree(indexList, HqdIndex::getId, HqdIndex::getParentId);
        if(indexList.size() != 1)
            throw new IllegalStateException("数据不符合树形结构，请检查数据");
        return indexList.get(0);
    }

    @Override
    public HqdIndex buildIndexTree(long verId) {
        List<HqdIndex> indexList = hqdIndexMapper.selectByVerId(verId);
        if(indexList.isEmpty()) return null;

        Map<Long, HqdIndex> indexMap = indexList.stream()
                .collect(Collectors.toMap(HqdIndex::getId, n -> n));
        List<HqdIndexExt> indexExtList = hqdIndexExtMapper.selectByIndexVerId(verId);
        Map<Long, List<HqdIndexExt>> indexExtMap = indexExtList.stream()
                .collect(Collectors.groupingBy(HqdIndexExt::getIndexId));
        List<HqdIndexArg> indexArgList = hqdIndexArgMapper.selectByIndexVerId(verId);
        Map<Long, List<HqdIndexArg>> indexArgMap = indexArgList.stream()
                .collect(Collectors.groupingBy(HqdIndexArg::getIndexId));

        for(HqdIndexArg indexArg: indexArgList) {
            if(indexArg.getValueOptionDictId() != null && indexArg.getValueOptionDictId() > 0) {
                indexArg.setValueOptions(listArgOptions(indexArg.getValueOptionDictId()));
            }
        }
        for(Iterator<HqdIndex> it = indexList.iterator(); it.hasNext(); ) {
            HqdIndex index = it.next();
            index.setIndexExts(indexExtMap.get(index.getId()));
            index.setIndexArgs(indexArgMap.get(index.getId()));

            if(index.getParentId() != null && index.getParentId() > 0) {
                HqdIndex parent = indexMap.get(index.getParentId());
                if(parent != null) {
                    index.setParent(parent);
                    parent.addChild(index);
                    it.remove();
                }
            }
        }
        if(indexList.size() != 1)
            throw new IllegalStateException("数据不符合树形结构，请检查数据");
        return indexList.get(0);
    }

    @Override
    public HqdIndex buildScoredIndexTree(long projectId, long unitId) {
        HqdProject hqdProject = hqdProjectMapper.selectById(projectId);
        if(hqdProject == null) return null;

        //List<UnitScore> scoreList = unitScoreMapper.selectScoresWhole(projectId, unitId);
        List<UnitScore> scoreList = unitService.getUnitScores(projectId, unitId);
        Map<Long, UnitScore> scoreMap = scoreList.stream()
                .collect(Collectors.toMap(UnitScore::getIndexId, n -> n));
        HqdIndex root = buildIndexTree(hqdProject.getIndexVerId());
        TreeUtils.traverse(root, hdqIndex -> hdqIndex.setUnitScore(scoreMap.get(hdqIndex.getId())));
        return root;
    }

    @Override
    @Transactional
    public HqdIndex saveIndex(HqdIndex hqdIndex) {
        if(hqdIndex.getId() != null) {
            hqdIndex.setUpdatedTime(new Date());
            hqdIndexMapper.updateById(hqdIndex);
        } else {
            hqdIndex.setCreatedTime(new Date());
            hqdIndexMapper.insert(hqdIndex);
        }
        if(!CollectionUtils.isEmpty(hqdIndex.getIndexExts())) {
            for(HqdIndexExt indexExt : hqdIndex.getIndexExts()) {
                indexExt.setIndexId(hqdIndex.getId());
                if(indexExt.getId() != null) {
                    hqdIndexExtMapper.updateById(indexExt);
                } else {
                    hqdIndexExtMapper.insert(indexExt);
                }
            }
        }
        if(!CollectionUtils.isEmpty(hqdIndex.getIndexArgs())) {
            for(HqdIndexArg indexArg : hqdIndex.getIndexArgs()) {
                indexArg.setIndexId(hqdIndex.getId());
                if(indexArg.getId() != null) {
                    hqdIndexArgMapper.updateById(indexArg);
                } else {
                    hqdIndexArgMapper.insert(indexArg);
                }
            }
        }
        return hqdIndex;
    }

    @Override
    public void cloneIndex(Integer copyFromVerId, int newVerId) {
        HqdIndex hqdIndex = buildIndexTree(copyFromVerId);
        TreeUtils.traverse(hqdIndex, idx -> {
            idx.setId(null);
            idx.setVerId(newVerId);
            HqdIndex p = idx.getParent();
            if(p != null) idx.setParentId(p.getId());
            idx.setUpdatedTime(null);
            if(!CollectionUtils.isEmpty(idx.getIndexExts())) {
                for(HqdIndexExt hqdIndexExt : idx.getIndexExts()) {
                    hqdIndexExt.setId(null);
                }
            }
            if(!CollectionUtils.isEmpty(idx.getIndexArgs())) {
                for(HqdIndexArg hqdIndexArg : idx.getIndexArgs()) {
                    hqdIndexArg.setId(null);
                }
            }
            saveIndex(idx);
        });
    }

    @Override
    @Transactional
    public int deleteIndex(long indexId) {
        int r = hqdIndexMapper.deleteById(indexId);
        r += hqdIndexExtMapper.deleteByIndexId(indexId);
        r += hqdIndexArgMapper.deleteByIndexId(indexId);
        return r;
    }

    @Override
    public int deleteIndexExt(long id) {
        return hqdIndexExtMapper.deleteById(id);
    }

    @Override
    public int deleteIndexArg(long id) {
        return hqdIndexArgMapper.deleteById(id);
    }

    @Override
    public HqdIndexVer getIndexVersion(int id) {
        return hqdIndexVerMapper.selectById(id);
    }

    @Override
    public List<HqdIndexVer> listIndexVersions(@Nullable String name, @Nullable Integer scope0) {
        LambdaQueryWrapper<HqdIndexVer> query = new QueryWrapper<HqdIndexVer>().lambda();
        if(StringUtils.hasText(name)) query.like(HqdIndexVer::getName, name);
        if(scope0 != null) query.ge(HqdIndexVer::getScope, scope0);
        return hqdIndexVerMapper.selectList(query);
    }

    @Override
    public HqdIndexVer saveIndexVersion(HqdIndexVer hqdIndexVer) {
        if(hqdIndexVer.getId() != null) {
            hqdIndexVer.setUpdatedTime(new Date());
            hqdIndexVerMapper.updateById(hqdIndexVer);
        } else {
            hqdIndexVer.setCreatedTime(new Date());
            hqdIndexVerMapper.insert(hqdIndexVer);
        }
        return hqdIndexVer;
    }

    @Override
    public Map<Integer, BiMap<String, Long>> getVerIdCodeIndexIdMapping(Collection<Integer> verIds) {
        return hqdIndexMapper.getVerIdCodeIdMapping(verIds).stream()
                .collect(Collectors.groupingBy(a -> (Integer)a[0], Collectors.toMap(a -> (String)a[1], a -> (Long)a[2], (a, b) -> b, HashBiMap::create)));
    }

    @Override
    public StatIndex getStatIndex(Long statIndexId) {
        return statIndexMapper.getStatIndex(statIndexId);
    }

    @Override
    public List<StatIndexItem> getStatIndexItems(Long statIndexId) {
        return statIndexItemMapper.getStatIndexItems(statIndexId);
    }

    @Override
    public List<StatIndex> getStatIndexes(int verId) {
        return statIndexMapper.getStatIndexes(verId);
    }

    @Override
    public List<StatIndex> listStatIndexes(@Nullable Collection<Integer> verIds, @Nullable Collection<String> codes, @Nullable String name) {
        return statIndexMapper.listStatIndexes(verIds, codes, name);
    }

    @Override
    @Transactional
    public StatIndex saveStatIndex(StatIndex statIndex) {
        if(statIndex.getId() != null) {
            statIndex.setUpdatedTime(new Date());
            statIndexMapper.updateById(statIndex);
        } else {
            statIndex.setCreatedTime(new Date());
            statIndexMapper.insert(statIndex);
        }
        if(!CollectionUtils.isEmpty(statIndex.getItems())) {
            for(StatIndexItem item : statIndex.getItems()) {
                item.setStatIndexId(statIndex.getId());
                if(item.getId() != null) {
                    statIndexItemMapper.updateById(item);
                } else {
                    statIndexItemMapper.insert(item);
                }
            }
        }
        return statIndex;
    }

    @Override
    public void cloneStatIndexes(Integer copyFromVerId, int verId) {
        Set<Integer> verIds = new HashSet<>();
        verIds.add(copyFromVerId);
        verIds.add(verId);
        Map<Integer, BiMap<String, Long>> verIdCodeIdMap = getVerIdCodeIndexIdMapping(verIds);
        List<StatIndex> statIndexes = getStatIndexes(copyFromVerId);
        for(StatIndex statIndex : statIndexes) {
            statIndex.setId(null);
            if(!CollectionUtils.isEmpty(statIndex.getItems())) {
                for(Iterator<StatIndexItem> it = statIndex.getItems().iterator(); it.hasNext(); ) {
                    StatIndexItem item = it.next();
                    item.setId(null);
                    String code = verIdCodeIdMap.get(copyFromVerId).inverse().get(item.getIndexId());
                    Long newIndexId = verIdCodeIdMap.get(verId).get(code);
                    if(newIndexId != null)
                        item.setIndexId(newIndexId);
                    else
                        it.remove();
                }
            }
            saveStatIndex(statIndex);
        }
    }

    @Override
    public int deleteStatIndex(long id) {
        int r = statIndexMapper.deleteById(id);
        r += statIndexItemMapper.deleteByStatIndexId(id);
        return r;
    }

    @Override
    public int deleteStatIndexItem(long id) {
        return statIndexItemMapper.deleteById(id);
    }

}
